﻿
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Text;
using agsXMPP;
using agsXMPP.protocol.client;
using sharpxbot.Core.Security;
using sharpxbot.Plugins.Base;
using sharpxbot.Plugins.BibleSearch.Data;
using sharpxbot.Plugins.BibleSearch.FuzzySearch;
using sharpxbot.Plugins.BibleSearch.Utils;
using sharpxbot.Plugins.Context;
using sharpxbot.Utils.Database;
namespace sharpxbot.Plugins.BibleSearch
{
    public class BibleSearchPlugin : AbstractPlugin
    {
        private static readonly Guid PluginGuid = new Guid("e6b4d99b-b0b5-4c89-b7ec-e67563553020");

        public const int MaxVersesCount = 5;
        public const string PluginDataPathSuffix = "BibleSearch/";

        #region IPlugin methods

        public override PluginPermissions NeededPermissions
        {
            get
            {
                return PluginPermissions.PluginOwner | PluginPermissions.Owners |
                    PluginPermissions.Members | PluginPermissions.Admins;
            }
        }

        public override string Name
        {
            get { return "BibleSearchPlugin"; }
        }

        public override string Author
        {
            get { return "tillias"; }
        }

        public override Guid GUID
        {
            get { return PluginGuid; }
        }

        public override string Description
        {
            get { return "Provides group-chat participants with access level of member and above to use full-text and fuxxy bible search"; }
        }

        public override string Usage
        {
            get
            {
                return
                  @"Each command starts with ** or *** symbols
                    1. Fuzzy search: ***some text
                    2. Full-text precise search: **some text
                    3. Verse search: **Мф.1:4
                    4. Verses range search: **Мф.1:4-5";
            }                            
        }

        #endregion

        public BibleSearchPlugin(IPluginContext context)
            : base(context)
        {
            database = new SQLiteDatabase();

            string pluginDataPath = context.PluginDataPath + "/" + PluginDataPathSuffix;

            database.Connect("Data Source=" + pluginDataPath + "bible.db;Version=3;");

            cache = new BibleCache(database);
            queryParser = new QueryParser();
            fullTextHelper = new FullTextHelper(pluginDataPath);
            util = new VerseUtil(cache);
        }

        public override void ProcessMessage(Message message)
        {
            if (message == null)
                return;

            if (IsGroupChatMessage(message) || IsPrivateGroupChatMessage(message))
            {
                Query query = queryParser.BuildQuery(message.Body);
                ExecuteQuery(message, query);
            }
        }

        public override void ProcessPresence(Presence presence)
        {
            //Nothing todo here
        }

        private void ExecuteQuery(Message message, Query query)
        {
            if (query != null)
            {
                //check agains usageCountersDictionary

                List<Verse> matches = PerformQuery(query);
                GenerateAndSendResponse(message, matches);
            }
        }

        private void GenerateAndSendResponse(Message message, List<Verse> matches)
        {
            if (matches.Count > 0)
            {
                StringBuilder sb = new StringBuilder();
                foreach (Verse v in matches)
                {
                    if (v.Relevantity > 0)
                    {
                        sb.AppendLine(String.Format("[{0}%]{1}{2}:{3} {4}",
                            v.Relevantity, v.BookNameAbbreviation, v.ChapterIndex, v.VerseIndex, v.Text));
                    }
                    else
                    {
                        sb.AppendLine(String.Format("{0}{1}:{2} {3}", v.BookNameAbbreviation, v.ChapterIndex, v.VerseIndex, v.Text));
                    }
                }

                SendResponse(sb.ToString(), message);
            }
        }

        private void SendResponse(string text, Message source)
        {
            Message response = new Message();
            response.Body = text;

            if (IsGroupChatMessage(source))
            {
                response.Type = MessageType.groupchat;
                response.To = new Jid(source.From.Bare);
            }
            else
                if (IsPrivateGroupChatMessage(source))
                {
                    response.Type = MessageType.chat;
                    response.To = source.From;
                }

            if (response.To != null)
            {
                Context.Channel.Send(response);
            }
        }

        private List<Verse> PerformQuery(Query query)
        {
            if (query == null)
                return new List<Verse>();

            switch (query.QueryType)
            {
                case QueryType.FuzzySearch:
                    return ExecuteFuzzyFullTextSearch(query);
                case QueryType.FullTextSearch:
                    return ExecuteFullTextSearch(query);
                case QueryType.WholeChapter:
                    return ExecuteWholeChapterSearch(query);
                case QueryType.SpecificVerse:
                    return ExecuteSpecificVerseSearch(query);
                case QueryType.VersesRange:
                    return ExecuteVersesRangeSearch(query);
                default:
                    return new List<Verse>();
            }
        }

        private List<Verse> ExecuteFullTextSearch(Query query)
        {
            List<Verse> result = new List<Verse>();

            string searchText = String.Format("{0}{1}{0}", '%', query.FullTextSearch); //"%" + request.Substring(2) + "%";

            DbCommand cmd = database.CreateCommand("select verse_index, text, chapter_id from verses where text like @verseText collate nocase");
            cmd.Parameters.Add(database.CreateCommandParameter("@verseText", searchText));

            DbDataReader reader = null;

            try
            {
                reader = cmd.ExecuteReader();

                int index = 0;
                while (reader.Read())
                {
                    if (index < MaxVersesCount)
                    {
                        result.Add(ReadVerse(reader));
                        ++index;
                    }
                    else
                        break;
                }
            }
            catch { }
            finally
            {
                if (reader != null)
                    reader.Close();
            }

            return result;
        }

        private List<Verse> ExecuteFuzzyFullTextSearch(Query query)
        {
            FuzzySearchAdapter adapter = new FuzzySearchAdapter(cache);
            return adapter.Convert(fullTextHelper.ExecuteSearch(query.FuzzySearch, 5));
        }

        private List<Verse> ExecuteWholeChapterSearch(Query query)
        {
            List<Verse> result = new List<Verse>();

            int bookID = cache.GetBookIDByAbbreviation(query.BookName);

            if (bookID > 0)
            {
                DbCommand cmd = database.CreateCommand("select verse_index, text, chapter_id from verses where chapter_id=" +
                                                        "(select id from chapters where book_id = @book_id and chapter_index = @chapter_index)");
                cmd.Parameters.Add(database.CreateCommandParameter("@book_id", bookID));
                cmd.Parameters.Add(database.CreateCommandParameter("@chapter_index", query.ChapterIndex));

                DbDataReader reader = null;

                try
                {
                    reader = cmd.ExecuteReader();

                    while (reader.Read())
                    {
                        result.Add(ReadVerse(reader));
                    }
                }
                catch { }
                finally
                {
                    if (reader != null)
                        reader.Close();
                }
            }

            return result;
        }

        private List<Verse> ExecuteSpecificVerseSearch(Query query)
        {
            List<Verse> result = new List<Verse>();

            int bookID = cache.GetBookIDByAbbreviation(query.BookName);

            if (bookID > 0)
            {
                DbCommand cmd = database.CreateCommand("select verse_index, text, chapter_id from verses where verse_index = @verse_index and chapter_id=" +
                                                        "(select id from chapters where book_id = @book_id and chapter_index = @chapter_index)");
                cmd.Parameters.Add(database.CreateCommandParameter("@book_id", bookID));
                cmd.Parameters.Add(database.CreateCommandParameter("@chapter_index", query.ChapterIndex));
                cmd.Parameters.Add(database.CreateCommandParameter("@verse_index", query.StartVerse));

                DbDataReader reader = null;

                try
                {
                    reader = cmd.ExecuteReader();

                    while (reader.Read())
                    {
                        result.Add(ReadVerse(reader));
                    }
                }
                catch { }
                finally
                {
                    if (reader != null)
                        reader.Close();
                }
            }

            return result;
        }

        private List<Verse> ExecuteVersesRangeSearch(Query query)
        {
            List<Verse> result = new List<Verse>();

            int bookID = cache.GetBookIDByAbbreviation(query.BookName);

            if (bookID > 0)
            {
                DbCommand cmd = database.CreateCommand("select verse_index, text, chapter_id from verses where " +  
                                                        "verse_index >= @start_verse_index and verse_index <= @end_verse_index and chapter_id=" +
                                                        "(select id from chapters where book_id = @book_id and chapter_index = @chapter_index)");
                cmd.Parameters.Add(database.CreateCommandParameter("@book_id", bookID));
                cmd.Parameters.Add(database.CreateCommandParameter("@chapter_index", query.ChapterIndex));
                cmd.Parameters.Add(database.CreateCommandParameter("@start_verse_index", query.StartVerse));
                cmd.Parameters.Add(database.CreateCommandParameter("@end_verse_index", query.EndVerse));

                DbDataReader reader = null;

                try
                {
                    reader = cmd.ExecuteReader();

                    while (reader.Read())
                    {
                        result.Add(ReadVerse(reader));
                    }
                }
                catch { }
                finally
                {
                    if (reader != null)
                        reader.Close();
                }
            }

            return result;
        }

        private Verse ReadVerse(DbDataReader reader)
        {
            Verse result = null;

            try
            {
                int verseIndex = reader.GetInt32(0);
                string verseText = reader.GetString(1);
                int chapterID = reader.GetInt32(2);

                Verse v = new Verse()
                {
                    VerseIndex = verseIndex,
                    ChapterID = chapterID,
                    Text = verseText
                };

                VerseInfo info = util.GetVerseInfo(v);
                if (info != null)
                {
                    v.ChapterIndex = info.ChapterIndex;
                    v.BookNameAbbreviation = info.BookNameAbbreviation;

                    result = v;
                }
            }
            catch { }

            return result;
        }

        IDatabase database;
        BibleCache cache;
        QueryParser queryParser;
        FullTextHelper fullTextHelper;
        VerseUtil util;


    }
}
